//二次封装etcd-client-api ,实现两种类型的客户端
    //1.服务注册客户端:向服务器新增服务信息数据,并进行保活
//封装思想:
    //1.服务注册客户端接口:向服务器新增数据进行保活
    //参数:注册中心地址(etcd服务器地址),新增的服务信息(服务器名-主机地址键值对)
#pragma once
#include<etcd/Client.hpp>
#include<etcd/KeepAlive.hpp>
#include<etcd/Response.hpp>
#include<etcd/Watcher.hpp>
#include<etcd/SyncClient.hpp>
#include<etcd/Value.hpp>
#include<thread>
#include"logger.hpp"
#include<functional>

class Registry
{
public:
    using ptr = std::shared_ptr<Registry>;
    Registry(const std::string& host)
        :_client(std::make_shared<etcd::Client> (host))
        ,_keep_alive(_client->leasekeepalive(3).get())
        ,_lease_id(_keep_alive->Lease())
    {}
    bool registry(const std::string& key,const std::string& value)
    {
        auto resp = _client->put(key,value,_lease_id).get();
        if(resp.is_ok() == false)
        {
            LOG_ERROR("新增数据失败:{}",resp.error_message());
            return false;
        }
        return true;
    }
    ~Registry(){}
private:
    std::shared_ptr<etcd::Client> _client;
    std::shared_ptr<etcd::KeepAlive> _keep_alive;
    int64_t _lease_id;
};
    


//2.服务发现客户端:从服务器查找服务信息数据,并进改变事件监控行

//2.服务发现客户端接口
    //提供两个设置回调函数接口:服务线上事件接口(数据新增),服务下线时间接口(数据删除)
    //提供一个设置根目录的接口:用于获取指定目录下的数据以及监控目录下的数据改变
class Discovery
{
public:
    using ptr = std::shared_ptr<Discovery>;
    using NotifyCallBack = function<void(std::string,std::string)>;
    Discovery(const std::string& host,const std::string& basedir,const NotifyCallBack& put_cb,\
        const NotifyCallBack& get_cb)
        :_client(std::make_shared<etcd::Client> (host))
        ,_put_cb(put_cb)
        ,_del_cb(get_cb)
        {
            // 发现之后进行回调
            //先进行服务发现,得到已有的数据
            auto resp = _client->ls(basedir).get();
            if(resp.is_ok() == false)
            {
                LOG_ERROR("获取数据失败{}",resp.error_message());
            }
            int sz = resp.keys().size();
            for(int i = 0 ; i <sz; i++)
            {
                // 没有调用 _put_cb函数,自行参考源码实现
                if (_put_cb) _put_cb(resp.key(i), resp.value(i).as_string());
                LOG_DEBUG("新增服务{}-{}",resp.value(i).as_string(),resp.key(i));
            }
            //再进行事件监控
            _watcher = std::make_shared<etcd::Watcher>(*_client.get(),basedir,std::bind(&Discovery::callbakck,\
            this,std::placeholders::_1),true);
        }
        ~Discovery()
        {
            _watcher->Cancel();
        }
private:
    void callbakck(const etcd::Response& resp)
    {
        if(resp.is_ok() == false)
        {
            LOG_ERROR("收到一个错误时间的通知{}",resp.error_message());
            return;
        }
        for(auto const& ev : resp.events())
        {
            if(ev.event_type() == etcd::Event::EventType::PUT)
            {
                if(_put_cb)
                {
                    _put_cb(ev.kv().key(),ev.kv().as_string());
                }
                LOG_DEBUG("新增服务{}-{}",ev.kv().key(),ev.kv().as_string())
    
            }
            else if (ev.event_type() == etcd::Event::EventType::DELETE_)
            {
                if(_del_cb)
                {
                    _del_cb(ev.prev_kv().key(),ev.kv().as_string());
                }
                LOG_DEBUG("下线服务{}-{}",ev.prev_kv().key(),ev.prev_kv().as_string())
            }
        }
    }
private:
    std::shared_ptr<etcd::Client> _client;   
    NotifyCallBack _put_cb;
    NotifyCallBack _del_cb;
    std::shared_ptr<etcd::Watcher> _watcher;  

};